package fr.obsmip.sedoo.server.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import fr.obsmip.sedoo.client.service.MetadataService;
import fr.obsmip.sedoo.core.RBVApplication;
import fr.obsmip.sedoo.core.dao.GeonetworkUserDAO;
import fr.obsmip.sedoo.server.service.dtotool.MetadataDTOTools;
import fr.obsmip.sedoo.shared.domain.ApplicationException;
import fr.obsmip.sedoo.shared.domain.DatasetDTO;
import fr.obsmip.sedoo.shared.domain.ExperimentalSiteDTO;
import fr.obsmip.sedoo.shared.domain.ExperimentalSiteSummaryDTO;
import fr.obsmip.sedoo.shared.domain.ExperimentalSiteSummaryNameProvider;
import fr.obsmip.sedoo.shared.domain.ObservatoryDTO;
import fr.obsmip.sedoo.shared.domain.ObservatorySummaryDTO;
import fr.obsmip.sedoo.shared.domain.ObservatorySummaryNameProvider;
import fr.obsmip.sedoo.shared.domain.SummaryDTO;
import fr.obsmip.sedoo.shared.domain.SummaryNameProvider;
import fr.sedoo.commons.client.util.StringUtil;
import fr.sedoo.commons.domain.User;
import fr.sedoo.commons.metadata.utils.dao.MetadataDAO;
import fr.sedoo.commons.metadata.utils.domain.MetadataTools;
import fr.sedoo.commons.metadata.utils.domain.SedooMetadata;
import fr.sedoo.commons.metadata.utils.domain.Summary;
import fr.sedoo.commons.shared.domain.GeographicBoundingBoxDTO;
import fr.sedoo.metadata.shared.domain.MetadataDTO;
import fr.sedoo.metadata.shared.domain.MetadataSummaryDTO;
import fr.sedoo.metadata.shared.domain.dto.I18nString;
import fr.sedoo.rbv.geonetwork.request.SearchCriteria;
import fr.sedoo.rbv.geonetwork.request.SearchRequest;

public class MetadataServiceImpl extends RemoteServiceServlet implements
MetadataService { 

	MetadataDAO dao = null;
	
	public MetadataServiceImpl() 
	{
		dao = RBVApplication.getInstance().getMetadataDAO();
	}
	
	
	@Override
	public String toXML(MetadataDTO metadataDTO, ArrayList<String> alternateLanguages) 
	{
		try {
			SedooMetadata metadata = MetadataDTOTools.toMetadata(metadataDTO, alternateLanguages);
			String xml = MetadataTools.toISO19139(metadata);
			return xml;
		} catch (Exception e) {
			return e.getMessage();
		}
	}

	@Override
	public MetadataDTO createDefaultMetadata() {
		
		return null;
	}

	@Override
	public String getPDFURL(String metadataId) throws ApplicationException 
	{
		try
		{
			return dao.getPDFURL(metadataId,null);
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}


	@Override
	public MetadataDTO getMetadataByUuid(String uuid, ArrayList<String> alternateLanguages) throws ApplicationException 
	{
		try
		{
			return MetadataDTOTools.toMetadatoDTO((SedooMetadata)dao.getMetadataById(uuid, null),alternateLanguages);
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}

	@Override
	public ObservatoryDTO getObservatoryByUuid(String uuid, ArrayList<String> displayLanguages) throws ApplicationException {
		try
		{
			ObservatoryDTO observatoryDTO = (ObservatoryDTO) MetadataDTOTools.toMetadatoDTO((SedooMetadata)dao.getMetadataById(uuid, null), new ObservatoryDTO(),displayLanguages);
			observatoryDTO.getChildrenPart().setChildren(getChildrenSummaries(observatoryDTO.getOtherPart().getUuid(), new ExperimentalSiteSummaryNameProvider(), displayLanguages));
			return observatoryDTO ;
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}


	private ArrayList<MetadataSummaryDTO> getChildrenSummaries(String parentUuid, SummaryNameProvider nameProvider, ArrayList<String> displayLanguages) throws ApplicationException 
	{
		SearchCriteria criteria = new SearchCriteria();
		criteria.setParentUuid(parentUuid);
		SearchRequest request = new SearchRequest();
		request.setDisplayLanguages(displayLanguages);
		request.setCriteria(criteria);
		request.setPagePosition(1);
		request.setPageSize(1000);
		
		boolean isCorrectlyExecuted = false;
		try {
			isCorrectlyExecuted = request.fetchSummaries();
		} catch (Exception e) {
			throw new ApplicationException("Uncorrect execution");
		}
		if (isCorrectlyExecuted == false)
		{
			throw new ApplicationException("Uncorrect execution");
		}
		else
		{
			ArrayList<MetadataSummaryDTO> result = new ArrayList<MetadataSummaryDTO>();
			List<Summary> summaries = request.getSummaries();
			for (Summary summary : summaries) {
				SummaryDTO tmp = MetadataDTOTools.toSummaryDTO(summary, displayLanguages);
				MetadataSummaryDTO aux = new MetadataSummaryDTO();
				aux.setUuid(summary.getUuid());
				aux.setName(nameProvider.getName(summary));
				aux.setDisplayAbstract(tmp.getResourceAbstract());
				result.add(aux);
			}
			return result;
		}
		
	}


	public List<SummaryDTO> getSummariesByDrainageBasinId(Long id) throws ApplicationException
	{
		return new ArrayList<SummaryDTO>();
	}


	private List<SummaryDTO> toSumaryDTOList(List<Summary> src, ArrayList<String> displayLanguages)
	{
		return MetadataDTOTools.toSummaryDTO(src, displayLanguages);
	}


	@Override
	public boolean deleteMetadataByUuid(String uuid) throws ApplicationException {

		try
		{
			User admin = GeonetworkUserDAO.getAdminUser();
			return dao.deleteMetadataByUuid(uuid, admin);
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}

	@Override
	public MetadataDTO saveObservatory(MetadataDTO metadataDTO, String observatoryName, ArrayList<String> displayLanguages) throws ApplicationException {
		ObservatoryDTO savedEntry = (ObservatoryDTO) saveEntry(metadataDTO, observatoryName, displayLanguages, new ObservatoryDTO());
		savedEntry.getChildrenPart().setChildren(getChildrenSummaries(savedEntry.getOtherPart().getUuid(), new ExperimentalSiteSummaryNameProvider(), displayLanguages));
		return savedEntry;
	}

	@Override
	public MetadataDTO saveExperimentalSite(MetadataDTO metadataDTO, String observatoryName, ArrayList<String> alternateLanguages) throws ApplicationException {
		ExperimentalSiteDTO savedEntry = (ExperimentalSiteDTO) saveEntry(metadataDTO, observatoryName, alternateLanguages, new ExperimentalSiteDTO());
		savedEntry.setParentSummary(((ExperimentalSiteDTO) metadataDTO).getParentSummary());
		savedEntry.getChildrenPart().setChildren(getDatasetsFromExperimentalSite(savedEntry.getOtherPart().getUuid(), savedEntry.getExperimentalSiteName(), observatoryName,alternateLanguages));
		return savedEntry;
	}
	
	@Override
	public MetadataDTO saveDataset(MetadataDTO metadataDTO, String observatoryName, ArrayList<String> alternateLanguages) throws ApplicationException {
		DatasetDTO savedEntry = (DatasetDTO) saveEntry(metadataDTO, observatoryName, alternateLanguages, new DatasetDTO());
		savedEntry.setObservatorySummary(((DatasetDTO) metadataDTO).getObservatorySummary());
		savedEntry.setExperimentalSiteSummary(((DatasetDTO) metadataDTO).getExperimentalSiteSummary());
		return savedEntry;
	}
	
	public MetadataDTO saveEntry(MetadataDTO metadataDTO, String observatoryName, List<String> alternateLanguages, MetadataDTO target) throws ApplicationException {
		try
		{
			User user = GeonetworkUserDAO.getUserFromObservatoryShortLabel(observatoryName);
			SedooMetadata metadata = MetadataDTOTools.toMetadata(metadataDTO, alternateLanguages);
			SedooMetadata savedMetadata = dao.saveMetadata(metadata, user);
			return MetadataDTOTools.toMetadatoDTO(savedMetadata, target, alternateLanguages);
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}

	@Override
	public ArrayList<ObservatorySummaryDTO> getObservatorySummaries(ArrayList<String> displayLanguages)
			throws ApplicationException {

		SearchCriteria criteria = new SearchCriteria();
		criteria.setIncludesObservatories(true);
		SearchRequest request = new SearchRequest();
		request.setDisplayLanguages(displayLanguages);
		request.setCriteria(criteria);
		//On force le retour de tous les observatoire
		request.setPagePosition(1);
		request.setPageSize(1000);
		
		boolean isCorrectlyExecuted = false;
		try {
			isCorrectlyExecuted = request.fetchSummaries();
		} catch (Exception e) {
			throw new ApplicationException("Uncorrect execution");
		}
		if (isCorrectlyExecuted == false)
		{
			throw new ApplicationException("Uncorrect execution");
		}
		else
		{
			ArrayList<ObservatorySummaryDTO> result = new ArrayList<ObservatorySummaryDTO>();
			List<Summary> summaries = request.getSummaries();
			for (Summary summary : summaries) {
				SummaryDTO tmp = MetadataDTOTools.toSummaryDTO(summary, displayLanguages);
				ObservatorySummaryDTO aux = new ObservatorySummaryDTO();
				aux.setUuid(summary.getUuid());
				ObservatorySummaryNameProvider nameProvider = new ObservatorySummaryNameProvider();
				aux.setName(nameProvider.getName(summary));
				aux.setDisplayAbstract(tmp.getResourceAbstract());
				aux.setDisplayTitle(tmp.getResourceTitle());
				result.add(aux);
			}
			return result;
		}
		
	}

	ObservatoryDTO getObservatoryByName(String name, ArrayList<String> alternateLanguages) throws ApplicationException
	{
		ArrayList<ObservatorySummaryDTO> observatorySummaries = getObservatorySummaries(alternateLanguages);
		Iterator<ObservatorySummaryDTO> iterator = observatorySummaries.iterator();
		while (iterator.hasNext()) 
		{
			ObservatorySummaryDTO current = (ObservatorySummaryDTO) iterator.next();
			if (current.getName().compareToIgnoreCase(name)==0)
			{
				return getObservatoryByUuid(current.getUuid(), alternateLanguages);
			}
		}
		
		return null;
	}

	@Override
	public boolean deleteObservatoryEntry(String uuid)
			throws ApplicationException {
		return deleteMetadataByUuid(uuid);
		// TODO Supprimer les fiches filles
	}


	@Override
	public boolean deleteExperimentalSiteEntry(String uuid)
			throws ApplicationException {
		return deleteMetadataByUuid(uuid);
		// TODO Supprimer les fiches filles
		// TODO Supprimer la référence dans le site père
	}


	@Override
	public ArrayList<ExperimentalSiteSummaryDTO> getExperimentalSitesSummaryFromParentUuid(
			String parentUuid, ArrayList<String> displayLanguages) throws ApplicationException {
		ArrayList<ExperimentalSiteSummaryDTO> result = new ArrayList<ExperimentalSiteSummaryDTO>();
		Iterator<MetadataSummaryDTO> aux = getChildrenSummaries(parentUuid, new ExperimentalSiteSummaryNameProvider(), displayLanguages).iterator();
		while (aux.hasNext()) {
			ExperimentalSiteSummaryDTO tmp = new ExperimentalSiteSummaryDTO();
			MetadataSummaryDTO current = aux.next();
			tmp.setDisplayAbstract(current.getDisplayAbstract());
			tmp.setName(current.getName());
			tmp.setUuid(current.getUuid());
			result.add(tmp);
		}
		return result;
	}

	
	@Override
	public ExperimentalSiteDTO getExperimentalSiteByUuid(String uuid, ArrayList<String> displayLanguages) throws ApplicationException 
	{
		try
		{
			ExperimentalSiteDTO experimentalSiteDTO = (ExperimentalSiteDTO) MetadataDTOTools.toMetadatoDTO((SedooMetadata)dao.getMetadataById(uuid, null), new ExperimentalSiteDTO(),displayLanguages);
			MetadataSummaryDTO observatorySummary = getObservatorySummary(experimentalSiteDTO.getParentSummary().getUuid(), displayLanguages);
			experimentalSiteDTO.setParentSummary(observatorySummary);
			experimentalSiteDTO.getChildrenPart().setChildren(getDatasetsFromExperimentalSite(uuid, experimentalSiteDTO.getExperimentalSiteName(), observatorySummary.getName(),displayLanguages));
			return experimentalSiteDTO ;
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}


	private MetadataSummaryDTO getObservatorySummary(String uuid, ArrayList<String> alternateLanguages) throws ApplicationException 
	{
		ObservatoryDTO dto = getObservatoryByUuid(uuid, alternateLanguages);
		MetadataSummaryDTO result = new MetadataSummaryDTO();
		result.setName(dto.getName());
		result.setUuid(uuid);
		return result;
	}
	
	private MetadataSummaryDTO getExperimentalSiteSummary(String uuid, ArrayList<String> alternateLanguages) throws ApplicationException 
	{
		ExperimentalSiteDTO dto = getExperimentalSiteByUuid(uuid, alternateLanguages);
		MetadataSummaryDTO result = new MetadataSummaryDTO();
		result.setName(dto.getName());
		result.setUuid(uuid);
		return result;
	}


	@Override
	public DatasetDTO getDatasetByUuid(String uuid, ArrayList<String> alternateLanguages) throws ApplicationException {
		try
		{
			DatasetDTO datasetDTO = (DatasetDTO) MetadataDTOTools.toMetadatoDTO((SedooMetadata)dao.getMetadataById(uuid, null), new DatasetDTO(),alternateLanguages);
			String experimentalSiteUuid = datasetDTO.getExperimentalSiteSummary().getUuid();
			if (StringUtil.isNotEmpty(experimentalSiteUuid))
			{
				try
				{
					MetadataSummaryDTO experimentalSiteSummary = getExperimentalSiteSummary(experimentalSiteUuid, alternateLanguages);
					datasetDTO.setExperimentalSiteSummary(experimentalSiteSummary);
				
					ExperimentalSiteDTO experimentalSite = getExperimentalSiteByUuid(experimentalSiteUuid, alternateLanguages);
					String observatoryUuid = experimentalSite.getParentSummary().getUuid(); 
				if (StringUtil.isNotEmpty(observatoryUuid))
				{
					datasetDTO.setObservatorySummary(getObservatorySummary(observatoryUuid, alternateLanguages));
				}
				}
				catch (Exception e)
				{
					//An error has occured while looking for hierarchy
				}
			}
			else
			{
				//Ancienne filiation, par nom de l'observatoire et du bassin
				String observatoryName = StringUtil.trimToEmpty(datasetDTO.getObservatorySummary().getName());
				String experimentalSiteName = StringUtil.trimToEmpty(datasetDTO.getExperimentalSiteSummary().getName());
				ObservatoryDTO observatory = getObservatoryByName(observatoryName, alternateLanguages);
				
				if (observatory != null)
				{
					//The observatory entry has been created
					datasetDTO.getObservatorySummary().setUuid(observatory.getOtherPart().getUuid());
					Iterator<MetadataSummaryDTO> iterator = observatory.getChildrenPart().getChildren().iterator();
					while (iterator.hasNext()) {
						MetadataSummaryDTO metadataSummaryDTO = (MetadataSummaryDTO) iterator.next();
						if (metadataSummaryDTO.getName().compareToIgnoreCase(experimentalSiteName)==0)
						{
							datasetDTO.getExperimentalSiteSummary().setUuid(metadataSummaryDTO.getUuid());
						}
					}
				}
			}
			
			return datasetDTO ;
		}
		catch (Exception e)
		{
			throw new ApplicationException(e.getMessage());
		}
	}


	@Override
	public ArrayList<GeographicBoundingBoxDTO> computeGeographicalBoxesFromExperimentalSites(
			String uuid) throws ApplicationException {
		ArrayList<GeographicBoundingBoxDTO> result = new ArrayList<GeographicBoundingBoxDTO>();
		if (StringUtils.isEmpty(uuid) == false)
		{
			ArrayList<ExperimentalSiteSummaryDTO> sites = getExperimentalSitesSummaryFromParentUuid(uuid, new ArrayList<String>());
			Iterator<ExperimentalSiteSummaryDTO> iterator = sites.iterator();
			while (iterator.hasNext()) 
			{
				ExperimentalSiteSummaryDTO experimentalSiteSummaryDTO = (ExperimentalSiteSummaryDTO) iterator.next();
				ExperimentalSiteDTO experimentalSite = getExperimentalSiteByUuid(experimentalSiteSummaryDTO.getUuid(), new ArrayList<String>());
				result.addAll(experimentalSite.getGeographicalLocationPart().getBoxes());
			}
		}
		return result;
	}
	
	


	@Override
	public String getExperimentalSiteUuidByDatasetUuid(String uuid) 
			throws ApplicationException {
		DatasetDTO aux = getDatasetByUuid(uuid, new ArrayList<String>());
		return aux.getExperimentalSiteSummary().getUuid();
	}

	@Override
	public String getObservatoryUuidByDatasetUuid(String uuid) 
			throws ApplicationException {
		DatasetDTO aux = getDatasetByUuid(uuid, new ArrayList<String>());
		return aux.getObservatorySummary().getUuid();
	}


	@Override
	public String getObservatoryUuidByExperimentalSiteUuid(String uuid)
			throws ApplicationException {
		ExperimentalSiteDTO aux = getExperimentalSiteByUuid(uuid, new ArrayList<String>());
		return aux.getParentSummary().getUuid();
	}


	/*
	 * Due to existing entries we can't just base the process on the parentUuid but also on the name of the experimental site. 
	 */
	private ArrayList<MetadataSummaryDTO> getDatasetsFromExperimentalSite(String parentUuid, String experimentalSiteName, String observatoryName,  ArrayList<String> displayLanguages) throws ApplicationException 
	{
		SearchCriteria criteria = new SearchCriteria();
		criteria.setIncludesDatasets(true);
		SearchRequest request = new SearchRequest(GeonetworkUserDAO.getUserFromObservatoryShortLabel(observatoryName));
		request.setDisplayLanguages(displayLanguages);
		request.setCriteria(criteria);
		request.setPagePosition(1);
		request.setPageSize(1000);
		
		boolean isCorrectlyExecuted = false;
		try {
			isCorrectlyExecuted = request.fetchSummaries();
		} catch (Exception e) {
			throw new ApplicationException("Uncorrect execution");
		}
		if (isCorrectlyExecuted == false)
		{
			throw new ApplicationException("Uncorrect execution");
		}
		else
		{
			ArrayList<MetadataSummaryDTO> result = new ArrayList<MetadataSummaryDTO>();
			List<Summary> summaries = request.getSummaries();
			for (Summary summary : summaries) {
				SummaryDTO tmp = MetadataDTOTools.toSummaryDTO(summary, displayLanguages);
				MetadataSummaryDTO aux = new MetadataSummaryDTO();
				if ((summary.getParentUuid() == null) && (summary.getExperimentalSiteName() == null))
				{
					continue;
				}
				if (summary.getParentUuid() != null)
				{
					if (summary.getParentUuid().compareTo(parentUuid)!=0)
					{
						continue;
					}
				}
				else if (summary.getExperimentalSiteName() != null)
				{
					if (summary.getExperimentalSiteName().compareToIgnoreCase(experimentalSiteName) != 0)
					{
						continue;
					}
				}
				
				//The dataset belongs to to experimental site
				aux.setUuid(summary.getUuid());
				aux.setName(tmp.getResourceTitle());
				aux.setDisplayAbstract(tmp.getResourceAbstract());
				result.add(aux);
			}
			return result;
		}
		
	}


	@Override
	public I18nString computeUseConditionsFromParent(String parentUuid, ArrayList<String> alternateLanguages)
			throws ApplicationException {
		MetadataDTO dto = getMetadataByUuid(parentUuid, alternateLanguages);
		return dto.getConstraintPart().getUseConditions();
	}


	@Override
	public I18nString computePublicAccessLimitationsFromParent(String parentUuid, ArrayList<String> alternateLanguages)
			throws ApplicationException {
		MetadataDTO dto = getMetadataByUuid(parentUuid, alternateLanguages);
		return dto.getConstraintPart().getPublicAccessLimitations();
	}
	
	
}

